package com.google.jessewilson.codesearch.googlecodesearch;

import com.google.jessewilson.codesearch.api.CodeFile;
import com.google.jessewilson.codesearch.api.Line;
import com.google.jessewilson.codesearch.api.Page;
import com.google.jessewilson.codesearch.api.Query;
import com.google.jessewilson.codesearch.api.SearchProvider;
import com.google.jessewilson.codesearch.api.SearchProviderException;
import com.google.jessewilson.codesearch.api.SearchResult;
import com.google.jessewilson.codesearch.api.spi.SearchProviderBackend;
import com.google.jessewilson.codesearch.util.ProducerSink;
import com.google.jessewilson.codesearch.util.StringToRegex;

import org.xml.sax.SAXException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;

/**
 * A search provider that uses Google Code Search (or a compatible API) as a
 * backend.
 *
 * @author jessewilson
 * @see <a href="http://code.google.com/apis/gdata/codesearch.html">Google Code
 *      Search API</a>
 */
class GcsSearchProviderBackend implements SearchProviderBackend {

  private static final String serviceUrl
      = "http://www.google.com/codesearch/feeds/search?as_q=";
  private static final String paramCaseSensitive = "as_case";
  private static final String paramPackages = "as_package";
  private static final String paramFileTypes = "as_filename";
  private static final String startIndex = "start-index";
  private static final String maxResults = "max-results";


  public SearchResult search(Query query, ProducerSink<Line> sink, Page page)
      throws SearchProviderException {
    StringBuilder targetUrl = new StringBuilder();
    targetUrl.append(serviceUrl);

    GcsSearchProviderParameters queryExtra = (GcsSearchProviderParameters) query.getSearchProviderParameters();

    String processedSearchTerm = query.getSearchTerm();
    if (queryExtra.convertToRegex()) {
      processedSearchTerm = new StringToRegex().regexify(processedSearchTerm);
    }
    targetUrl.append(processedSearchTerm);

    // limit the query to only results within this range
    targetUrl.append("&").append(startIndex).append("=").append(page.getStart() + 1);
    targetUrl.append("&").append(maxResults).append("=").append(page.getEnd() - page.getStart());

    // pull extra query details from the query
    if(queryExtra != null) {
      String fileTypes = queryExtra.getFileTypes().trim();
      if(fileTypes.length() > 0) {
        targetUrl.append("&").append(paramFileTypes).append("=").append(fileTypes);
      }
      String packages = queryExtra.getPackages().trim();
      if(packages.length() > 0) {
        targetUrl.append("&").append(paramPackages).append("=").append(packages);
      }
      boolean isCaseSensitive = queryExtra.isCaseSensitive();
      if(isCaseSensitive) {
        // case sensitivity is off by default
        targetUrl.append("&").append(paramCaseSensitive).append("=").append("y");
      }
    }

    try {
      // establish a connection
      InputStream in = new URL(targetUrl.toString()).openConnection()
          .getInputStream();

      // do the search
      SearchResult searchResult = new EntryParser(this).parse(in, sink, page);

      // post process the results
      searchResult.setHasMoreResults(searchResult.getTotalResults() > page.getEnd());

      return searchResult;
    } catch(EndOfResultsException e) {
      throw new SearchProviderException(e);

    } catch (IOException e) {
      throw new SearchProviderException(e);
    }
  }

  public void downloadFile(CodeFile source, File target)
      throws SearchProviderException {
    try {
      URL url = new URL(source.getId());
      URLConnection urlConnection = url.openConnection();
      InputStream inputStream = urlConnection.getInputStream();
      String contentType = urlConnection.getHeaderField("Content-Type");
      String characterEncoding = "UTF-8"; // TODO(jessewilson): get the correct content type
      Reader htmlIn = new InputStreamReader(inputStream, characterEncoding);

      final Writer textOut = new FileWriter(target);
      new FileDownloadHtmlToCode(htmlIn, textOut).extractCode();

    } catch (SAXException e) {
      throw new SearchProviderException(e);
    } catch (IOException e) {
      throw new SearchProviderException(e);
    }
  }

  public SearchProvider getSearchProvider() {
    return new GcsSearchProvider();
  }
}
